/*
 ============================================================================
 Name        : AlgoritmosPlanificacion.c
 Author      : SOogle
 Version     : 1.0
 Copyright   : All rights reserved
 Description : FUSELAGE - FUSE FAT32 FILE SYSTEM
 ============================================================================
 */

#include "../../headers/GlobalHeader.h"

extern t_Cola *colaPedidos;
extern t_Cola *colaActiva;
extern int32_t SUBIENDO;
extern struct_config configuracion;
extern PPD_CHS posicionActualCabezal;

t_Cola *crearCola()
{
	int32_t rc;
	t_Cola *nuevaCola;
	nuevaCola = malloc(sizeof(t_Cola));
	nuevaCola->frente = NULL;
	nuevaCola->fin = NULL;

	rc = sem_init(&(nuevaCola->cantidadPedidos), 1, 0);
	if (rc == -1)
	{
		free(nuevaCola);
		return NULL;
	}

	return nuevaCola;
}

void agregarPedido_cola(t_pedido pedidoNuevo)
{
	t_nodoCola *nuevoNodo = malloc(sizeof(t_nodoCola));
	nuevoNodo->pedido = pedidoNuevo;
	nuevoNodo->siguiente = NULL;

	if (colaPedidos->frente == NULL)
	{
		colaPedidos->frente = nuevoNodo;
	}
	else
	{
		colaPedidos->fin->siguiente = nuevoNodo;
	}

	colaPedidos->fin = nuevoNodo;

	sem_post(&(colaPedidos->cantidadPedidos));
}

t_pedido quitarPedido_SSTF()
{
	t_pedido pedidoElegido;
	float mejorTiempo, tiempoEvaluo;
	t_nodoCola *aux, *nodoBorro, *anterior, *anteriorBorro;

	mejorTiempo = 9999999999;
	aux = colaPedidos->frente;
	nodoBorro = aux;
	anterior = NULL;
	anteriorBorro = NULL;

	//sem_wait(&colaPedidos->cantidadPedidos);

	while(aux != NULL)
	{
		PPD_CHS sectorEvaluo = ppd_LBAToCHS(aux->pedido.direccionSector);
		tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

		if (tiempoEvaluo < mejorTiempo)
		{
			mejorTiempo = tiempoEvaluo;
			pedidoElegido = aux->pedido;
			nodoBorro = aux;
			anteriorBorro = anterior;
		}
		//ME ASEGURO QUE HAYA MAS NODOS EN LA COLA, MUEVO EL ANTERIOR Y MUEVO EL AUXILIAR
		if(aux->siguiente != NULL)
		{
			anterior = aux;
			aux = aux->siguiente;
		}
		else//NO HAY MAS NODOS EN LA COLA, AUX ESTA APUNTANDO AL ULTIMO NODO
		{
			aux = NULL;
		}

	}

	if (anteriorBorro != NULL)
	{
		if(colaPedidos->fin == nodoBorro)
		colaPedidos->fin = anteriorBorro;

		anteriorBorro->siguiente = nodoBorro->siguiente;
	}
	else //ESTOY BORRANDO EL PRIMER NODO DE LA COLA
	{
		colaPedidos->frente = nodoBorro->siguiente;
	}

	free(nodoBorro);

	return pedidoElegido;
}

void destruirCola(t_Cola *colaDestroy)
{
	sem_destroy(&colaDestroy->cantidadPedidos);

	free(colaDestroy);
}

PPD_CHS proximoPedido_SSTF()
{
	PPD_CHS proximoSector;
	t_pedido pedidoElegido;
	float mejorTiempo, tiempoEvaluo;
	t_nodoCola *aux;

	mejorTiempo = 9999999999;
	aux = colaPedidos->frente;
	proximoSector = ppd_LBAToCHS(-1);

	while(aux != NULL)
	{
		PPD_CHS sectorEvaluo = ppd_LBAToCHS(aux->pedido.direccionSector);
		tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

		if (tiempoEvaluo < mejorTiempo)
		{
			pedidoElegido = aux->pedido;
			proximoSector = ppd_LBAToCHS(pedidoElegido.direccionSector);
			mejorTiempo = tiempoEvaluo;
		}
		if(aux->siguiente != NULL)
		{
			aux = aux->siguiente;
		}
		else//NO HAY MAS NODOS EN LA COLA, AUX ESTA APUNTANDO AL ULTIMO NODO
		{
			aux = NULL;
		}
	}

	return proximoSector;
}

t_Cola *armarColaActiva()
{
	int32_t i;
	t_nodoCola *anterior;

	//SI HAY PEDIDOS, PUEDO ARMAR COLA ACTIVA
	if(colaPedidos->frente != NULL)
	{
		colaActiva->frente = colaPedidos->frente;
		colaActiva->fin = colaPedidos->frente;
	}

	i = 1;

	while(i <= configuracion.tamanioListaScan && colaPedidos->frente != NULL)
	{
		anterior = colaPedidos->frente;
		colaActiva->fin = anterior;

		//RESTO LA CANTIDAD DE PEDIDOS EN LA COLA DE PEDIDOS Y AUMENTO LA CANTIDAD EN LA COLA ACTIVA
		sem_wait(&colaPedidos->cantidadPedidos);
		sem_post(&colaActiva->cantidadPedidos);

		//AVANZO EL PUNTERO, SI ES NULL NO ME QUEDAN MAS PEDIDOS EN LA COLA DE PEDIDOS
		colaPedidos->frente = colaPedidos->frente->siguiente;
		if (colaPedidos->frente == NULL)
		{
			colaPedidos->fin = NULL;
		}

		i++;
	}

	//EL SIGUIENTE DE LA COLA QUE TRABAJO SIEMPRE ES NULL
	colaActiva->fin->siguiente = NULL;

	return colaActiva;
}

t_pedido quitarPedido_NSTEP()
{
	t_pedido pedidoElegido, unicoPedido;
	t_nodoCola *aux, *nodoBorro, *anterior, *anteriorBorro;
	float tiempoEvaluo, mejorTiempoSubiendo, mejorTiempoBajando;
	int32_t PEDIDO_ARRIBA, PEDIDO_ABAJO;

	PEDIDO_ARRIBA = PEDIDO_ABAJO = 0;

	mejorTiempoSubiendo = 9999999999;
	mejorTiempoBajando = 9999999999;

	aux = colaActiva->frente;
	nodoBorro = aux;
	anterior = NULL;
	anteriorBorro = NULL;

	while(SUBIENDO)
	{
		while(aux != NULL)
		{
			PPD_CHS sectorEvaluo = ppd_LBAToCHS(aux->pedido.direccionSector);

			//SI ESTOY SUBIENDO TENGO QUE ELEGIR EL PEDIDO QUE ESTA MAS CERCA, TANTO DE PISTAS SUPERIORES O EN LA MISMA PISTA
			if(sectorEvaluo.cylinder >= posicionActualCabezal.cylinder)
			{
				PEDIDO_ARRIBA ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoSubiendo)
				{
					mejorTiempoSubiendo = tiempoEvaluo;
					pedidoElegido = aux->pedido;
					nodoBorro = aux;
					anteriorBorro = anterior;
				}
			}
			else
			{
				PEDIDO_ABAJO ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoBajando)
				{
					mejorTiempoBajando = tiempoEvaluo;
					unicoPedido = aux->pedido;
					nodoBorro = aux;
					anteriorBorro = anterior;
				}
			}
			anterior = aux;
			aux = aux->siguiente;
		}

		//TENGO QUE BORRAR UN NODO INTERMEDIO O EL ULTIMO
		if (anteriorBorro != NULL)
		{
			if(colaPedidos->fin == nodoBorro)
			colaPedidos->fin = anteriorBorro;

			anteriorBorro->siguiente = nodoBorro->siguiente;
		}
		//TENGO QUE BORRAR EL PRIMER NODO DE LA COLA ACTIVA
		else
		{
			colaActiva->frente = nodoBorro->siguiente;
		}

		sem_wait(&colaActiva->cantidadPedidos);
		free(nodoBorro);

		if (PEDIDO_ARRIBA == 0 && PEDIDO_ABAJO > 0)
		{
			SUBIENDO = FALSE;
			return unicoPedido;
		}
		else
		{
			return pedidoElegido;
		}
	}

	//ATIENDO LOS PEDIDOS DE PISTAS INFERIORES, PORQUE ESTOY BAJANDO AHORA
	while(!SUBIENDO)
	{
		while(aux != NULL)
		{
			PPD_CHS sectorEvaluo = ppd_LBAToCHS(aux->pedido.direccionSector);

			//SI ESTOY SUBIENDO TENGO QUE ELEGIR EL PEDIDO QUE ESTA MAS CERCA, TANTO DE PISTAS SUPERIORES O EN LA MISMA PISTA
			if(sectorEvaluo.cylinder <= posicionActualCabezal.cylinder)
			{
				PEDIDO_ABAJO ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoBajando)
				{
					mejorTiempoBajando = tiempoEvaluo;
					pedidoElegido = aux->pedido;
					nodoBorro = aux;
					anteriorBorro = anterior;
				}

			}
			else
			{
				PEDIDO_ARRIBA ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoSubiendo)
				{
					mejorTiempoSubiendo = tiempoEvaluo;
					unicoPedido = aux->pedido;
					nodoBorro = aux;
					anteriorBorro = anterior;
				}
			}

			anterior = aux;
			aux = aux->siguiente;
		}

		//TENGO QUE BORRAR UN NODO INTERMEDIO O EL ULTIMO
		if (anteriorBorro != NULL)
		{
			if(colaPedidos->fin == nodoBorro)
			colaPedidos->fin = anteriorBorro;

			anteriorBorro->siguiente = nodoBorro->siguiente;
		}
		//TENGO QUE BORRAR EL PRIMER NODO DE LA COLA ACTIVA
		else
		{
			colaActiva->frente = nodoBorro->siguiente;
		}

		sem_wait(&colaActiva->cantidadPedidos);
		free(nodoBorro);

		if (PEDIDO_ABAJO == 0 && PEDIDO_ARRIBA > 0)
		{
			SUBIENDO = TRUE;
			return unicoPedido;
		}
		else
		{
			return pedidoElegido;
		}
	}
}


PPD_CHS proximoPedido_NSTEP()
{
	PPD_CHS proximoSector;
	t_pedido pedidoElegido, unicoPedido;
	t_nodoCola *aux, *anterior;
	float tiempoEvaluo, mejorTiempoSubiendo, mejorTiempoBajando;
	int32_t PEDIDO_ARRIBA, PEDIDO_ABAJO;

	PEDIDO_ARRIBA = PEDIDO_ABAJO = 0;

	mejorTiempoSubiendo = 9999999999;
	mejorTiempoBajando = 9999999999;
	aux = colaActiva->frente;
	proximoSector = ppd_LBAToCHS(-1);

	while(SUBIENDO)
	{
		while(aux != NULL)
		{
			PPD_CHS sectorEvaluo = ppd_LBAToCHS(aux->pedido.direccionSector);

			//SI ESTOY SUBIENDO TENGO QUE ELEGIR EL PEDIDO QUE ESTA MAS CERCA, TANTO DE PISTAS SUPERIORES O EN LA MISMA PISTA
			if(sectorEvaluo.cylinder >= posicionActualCabezal.cylinder)
			{
				PEDIDO_ARRIBA ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoSubiendo)
				{
					mejorTiempoSubiendo = tiempoEvaluo;
					pedidoElegido = aux->pedido;
					proximoSector = ppd_LBAToCHS(pedidoElegido.direccionSector);
				}
			}
			else
			{
				PEDIDO_ABAJO ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoBajando)
				{
					mejorTiempoBajando = tiempoEvaluo;
					unicoPedido = aux->pedido;
					proximoSector = ppd_LBAToCHS(unicoPedido.direccionSector);
				}
			}
			anterior = aux;
			aux = aux->siguiente;
		}

		return proximoSector;
	}

	//ATIENDO LOS PEDIDOS DE PISTAS INFERIORES, PORQUE ESTOY BAJANDO AHORA
	while(!SUBIENDO)
	{
		while(aux != NULL)
		{
			PPD_CHS sectorEvaluo = ppd_LBAToCHS(aux->pedido.direccionSector);

			//SI ESTOY SUBIENDO TENGO QUE ELEGIR EL PEDIDO QUE ESTA MAS CERCA, TANTO DE PISTAS SUPERIORES O EN LA MISMA PISTA
			if(sectorEvaluo.cylinder <= posicionActualCabezal.cylinder)
			{
				PEDIDO_ABAJO ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoBajando)
				{
					mejorTiempoBajando = tiempoEvaluo;
					pedidoElegido = aux->pedido;
					proximoSector = ppd_LBAToCHS(pedidoElegido.direccionSector);
				}

			}
			else
			{
				PEDIDO_ARRIBA ++;
				tiempoEvaluo = tiempoEntreSectores(posicionActualCabezal, sectorEvaluo);

				if (tiempoEvaluo < mejorTiempoSubiendo)
				{
					mejorTiempoSubiendo = tiempoEvaluo;
					unicoPedido = aux->pedido;
					proximoSector = ppd_LBAToCHS(unicoPedido.direccionSector);
				}
			}

			anterior = aux;
			aux = aux->siguiente;
		}

		return proximoSector;
	}
}
